﻿using System;
using MathService.DSP.Fourier;

namespace MathService.DSP.Signals
{
    public partial class DigitalSignal
    {
        /// <summary>Цифровой сигнал - интеграл от текущего сигнала</summary>
        /// <returns>Интегральный сигнал без интерполяции</returns>
        public DigitalSignal GetIntegralSignal()
        {
            var lv_N = f_Samples.Length;
            var lv_dx = f_dx;
            var lv_Result = new DigitalSignal(lv_dx, lv_N);

            var lv_Summ = .0;
			//int i = 0;
			//Array.ForEach(Samples, V => Result.f_Samples[i++] = Summ += V*lv_dx);
            for(var n = 0; n < lv_N; n++) lv_Result.f_Samples[n] = lv_Summ += this[n] * lv_dx;

            return lv_Result;
        }

        /// <summary>Возведение сигнала в степень</summary>
        /// <param name="power">Показатель степени</param>
        /// <returns>
        /// Цифровой сигнал, каждый отсчёт которого равен отсчётам текущего сигнала, возведённым в степень
        /// </returns>
        public DigitalSignal PowerOf(double power)
        {
            var lv_Result = new DigitalSignal(this);

			//int i = 0;
			//Array.ForEach(f_Samples, Value => Result.f_Samples[i++] = Math.Pow(Value, Power));
        	var i_Length = lv_Result.f_Samples.Length;
        	for(var i = 0; i < i_Length; i++)
                lv_Result.f_Samples[i] = Math.Pow(this[i], power);

            return lv_Result;
        }

        /// <summary>Инвертирование сигнала по времени</summary>
        /// <returns>Цифровой сигнал, инвертированный по времени</returns>
        public DigitalSignal TimeInverse()
        {
            var lv_Values = new double[f_Samples.Length];
        	var i_Length = lv_Values.Length;
        	for(var i = 0; i < i_Length; i++) lv_Values[i] = this[i_Length - i - 1];
			return new DigitalSignal(f_dx, lv_Values) { x0 = f_x0 };
        }

        /// <summary>Кореляционный интеграл</summary>
        /// <param name="h">Сигнал, с которым производится кореляция</param>
        /// <returns>Цифровой сигнал, равный кореляционному интегралу между сигналами</returns>
        public DigitalSignal Corelate(DigitalSignal h)
        {
            var lv_Result = new DigitalSignal(dx, f_Samples.Length);

        	var n_Length = f_Samples.Length;
        	for(var n = 0; n < n_Length; n++)
                for(var i = 0; i + n < n_Length && i < h.f_Samples.Length; i++)
                    lv_Result[n] += this[n + i] * h[i];
            return lv_Result;
        }

        public double SqrtError(DigitalSignal s)
        {
            Exception Error = null;
            if(s.f_dx != f_dx)
                Error = new ArgumentException("Нельзя расчитать среднеквадратическую ошибку для сигналов с разной частотой дискретизации");
            if(s.f_Samples.Length != f_Samples.Length)
                Error = new ArgumentException("Нельзя расчитать среднеквадратическую ошибку для сигналов разной длительности", Error);
            if(s.f_x0 != f_x0)
                Error = new ArgumentException("Нельзя расчитать среднеквадратическую ошибку для сигналов с разным временным смещением", Error);

            if(Error != null) throw Error;

            var lv_SqrtError = .0;
            var lv_Dx = f_dx;
        	var i_Length = f_Samples.Length;
        	for(var i = 0; i < i_Length; i++)
        	{
        		var lv_E = s[i] - this[i];
        		lv_SqrtError += lv_E * lv_E * lv_Dx;
        	}
        	return lv_SqrtError;
        }

        public DigitalSignal GetNormalized()
        {
            return this / GetMax();
        }

        public virtual void Normalize()
        {
            Samples = GetNormalized().Samples;
        }

        public SpectrumSignal GetSpectrum()
        {
            return new SpectrumSignal(Length.GetInverse(), Samples.FurierTransform());
        }

        public SpectrumSignal GetSpectrumFast()
        {
            return new SpectrumSignal(Length.GetInverse(), Samples.FastFurierTransform());
        }

        public DigitalSignal GetError(DigitalSignal s)
        {
            var lv_X0 = Math.Max(x0, s.x0);
            var lv_Dx = Math.Min(dx, s.dx);
            var lv_X1 = Math.Min(x1, s.x1);
            var e = new DigitalSignal(lv_Dx, lv_X0, lv_X1 - lv_X0);

        	var lv_ESamples = e.Samples;
        	var i_Length = lv_ESamples.Length;
        	for(var i = 0; i < i_Length; i++)
            {
            	var x = lv_Dx * i + lv_X0;
            	lv_ESamples[i] = Math.Abs(this[x] - s[x]);
            }
        	return e;
        }
    }
}